import math
import os
import re
import datetime
import string
from pathlib import Path

import numpy as np
import pandas as pd
from functools import reduce
from pandas import DataFrame, Series
from datetime import datetime

pd.set_option('display.unicode.east_asian_width', True)

BASE_PATH_微服务 = r'D:\100-Project\2024-2025-2学期安排\武昌首义学院\14301052_《微服务应用开发技术（企业）》'


def check_filename_format(filename_list):
    """
    检查字符串列表中的每个字符串是否符合六部分格式

    参数:
    strings : 要检查的字符串列表

    输出:
    打印所有不符合规则的字符串
    """
    invalid_strings = []
    pattern = r'^[^-\.]+-[^-\.]+-[^-\.]+\.[^-\.]+\.[^-\.]+\.[^-\.]+$'

    for s in filename_list:
        if not bool(re.match(pattern, s)):
            invalid_strings.append(s)

    if invalid_strings:
        print("以下字符串不符合格式要求:")
        for s in invalid_strings:
            print(f"  - {s}")
        print(f"\n总计: {len(invalid_strings)} 个字符串不符合规则")
        exit(0)
    else:
        # print("所有字符串都符合格式要求!")
        pass


def get_files_name(folder_path, return_type="name_only") -> Series:
    """
    获得指定目录下的所有文件的名称
    返回:
        list: 包含所有 .docx 文件名的列表
    """
    try:
        # 验证文件夹是否存在
        if not os.path.isdir(folder_path):
            print(f"错误: 文件夹不存在 - {folder_path}")
            return pd.Series([], name='文件名')

        folder = Path(folder_path)
        docx_files = []

        iterator = folder.iterdir()

        # 收集所有 .docx 文件
        for item in iterator:
            if item.is_file() and item.suffix.lower() == '.docx':
                # 根据返回类型处理路径
                if return_type == "full_path":
                    docx_files.append(str(item.absolute()))
                elif return_type == "name_only":
                    docx_files.append(item.name)
                elif return_type == "relative":
                    docx_files.append(str(item.relative_to(folder)))
                else:
                    docx_files.append(str(item.absolute()))

        # 按文件名排序
        docx_files.sort()

        print(f"在 {folder_path} 中找到 {len(docx_files)} 个文件")

        check_filename_format(docx_files)
        return pd.Series(docx_files, name="文件名")


    except Exception as e:
        print(f"获取文件列表时出错: {str(e)}")
        return pd.Series([], name='文件名')


def extract_parts_from_series(series, part_index):
    """
    从符合格式的字符串Series中提取指定部分

    参数:
    series : pandas Series, 包含格式为 "aaa-bbb-ccc.ddd.eee.fff" 的字符串
    part_index : 要提取的部分索引 (1-6)

    返回:
    pd.Series : 包含提取部分的Series

    异常:
    ValueError: 当part_index不在1-6范围内
    """
    # 验证部分索引
    if part_index < 1 or part_index > 6:
        raise ValueError("part_index 必须在 1 到 6 之间")

    # 编译高效的正则表达式
    pattern = re.compile(r'^([^-\.]+)-([^-\.]+)-([^-\.]+)\.([^-\.]+)\.([^-\.]+)\.([^-\.]+)$')

    # 提取函数
    def extract_part(s):
        match = pattern.match(s)
        if match:
            return match.group(part_index)
        return None  # 不符合格式的返回None

    # 应用提取函数并返回Series
    return series.apply(extract_part)


def get_学号_segment(files_name: Series) -> Series:
    se = extract_parts_from_series(files_name, 2)
    if find_duplicates(se):
        exit(0)
    se.name = '学号'
    se.astype(int)
    return se


def get_姓名_segment(files_name: Series) -> Series:
    se = extract_parts_from_series(files_name, 3)
    if find_duplicates(se):
        exit(0)
    se.name = '姓名'
    se.astype(str)
    return se


def get_real_日期_segment(files_name: Series) -> Series:
    se = extract_parts_from_series(files_name, 4)
    se.name = '提交日期'
    se.astype(str)
    return se


def get_logical_日期_segment(files_name: Series) -> Series:
    se = extract_parts_from_series(files_name, 5)
    se.name = '提交日期'
    se.astype(str)
    return se


"""
def combine_series_to_dataframe(*series_list) -> DataFrame:
    return pd.concat(series_list, axis=1)
"""


def get_学生_信息() -> DataFrame:
    try:
        file = f"{BASE_PATH_微服务}\学生名单.xlsx"
        # 读取Excel文件中的指定列
        df = pd.read_excel(
            file,
            usecols=['学号', '姓名'],
            dtype={'学号': int, '姓名': str},
            header=0
        )

        # 打印读取的信息
        print(f"✅ 成功读取文件: {BASE_PATH_微服务}\学生名单.xlsx")
        # print(f"读取的列: {df.columns.tolist()} \t 总行数: {len(df)}")

        return df

    except Exception as e:
        print(f"❌ 读取失败: {str(e)}")
        raise


def find_duplicates(items: Series) -> bool:
    """查找并打印Series中的重复项"""
    # 计算每个值的出现次数
    value_counts = items.value_counts()

    # 找出重复项（出现次数大于1）
    duplicates = value_counts[value_counts > 1]

    if not duplicates.empty:
        print(f"列表中有 {len(duplicates)} 个重复项:")
        for i, (value, count) in enumerate(duplicates.items(), 1):
            print(f"{i}. {value} (出现次数: {count})")
        return True
    else:
        return False


def merge_student_dataframes(*dfs):
    """
    合并多个包含学生数据的DataFrame

    参数:
    *dfs : 可变数量的DataFrame，每个DataFrame必须包含'学号'和'姓名'列

    返回:
    pd.DataFrame : 合并后的DataFrame，包含学号、姓名和所有科目列

    异常:
    ValueError: 如果输入少于两个DataFrame
    """
    if len(dfs) < 2:
        raise ValueError("至少需要两个DataFrame进行合并")

    # 1. 提取所有DataFrame中的科目列（排除学号和姓名）
    all_subject_columns = set()
    for df in dfs[1:]:  # 从第二个DataFrame开始
        subject_cols = [col for col in df.columns if col not in ['学号', '姓名']]
        all_subject_columns.update(subject_cols)

    # 2. 创建基础表（只保留学号和姓名）
    base_df = dfs[0][['学号', '姓名']].copy()

    # 3. 使用reduce函数逐步合并所有DataFrame
    def merge_func(left, right):
        # 从右侧DataFrame中提取学号和科目列
        right_subject_cols = [col for col in right.columns if col in all_subject_columns]
        right_subset = right[['学号'] + right_subject_cols]
        right_subset['学号'].astype(int)

        # 左连接合并
        return pd.merge(left, right_subset, on='学号', how='left')

    # 应用reduce进行逐步合并
    merged_df = reduce(merge_func, [base_df] + list(dfs[1:]))

    return merged_df


def add_year_prefix_enhanced(series: Series, year: str) -> Series:
    # 转换为字符串并验证格式
    str_series = series.astype(str)

    # 检查是否为四位数字
    mask = str_series.str.match(r'^\d{4}$')

    # 添加前缀
    result = year + str_series

    # 标记无效输入
    result[~mask] = "无效格式: " + str_series[~mask]

    return result


def convert_mmdd_to_date(series):
    yyyymmdd = add_year_prefix_enhanced(series, "2025")

    # 定义转换函数
    def convert_single_date(yyyymmdd):
        try:
            # 提取月份和日期
            year = int(yyyymmdd[:4])
            month = int(yyyymmdd[4:6])
            day = int(yyyymmdd[6:])

            # 创建日期对象（使用固定年份1900）
            return datetime(year, month, day)
        except (ValueError, TypeError):
            # 处理无效格式
            return pd.NaT

    # 应用转换函数
    return yyyymmdd.apply(convert_single_date)


def calculate_date_scores(se_date: Series, base_score=25) -> Series:
    """
    计算日期数组中每个日期的评分
    """

    # 计算datetime Series中每个日期与最早日期的天数差

    # 1. 找到最早日期
    min_date = se_date.min()

    # 2. 计算每个日期与最早日期的天数差
    days_diff = (se_date - min_date).dt.days

    return base_score - (days_diff + 1) // 2


def merge_student_homework(df_stu_info, df_homework, output_path=None,
                           id_col='学号', name_col='姓名', score_col='最终成绩'):
    """
    合并学生信息与作业成绩数据

    参数:
        df_stu_info (pd.DataFrame): 学生信息DataFrame（完整学生列表）
        df_homework (pd.DataFrame): 学生作业成绩DataFrame
        output_path (str): 输出Excel文件路径（可选）
        id_col (str): 学号列名（默认为'学号'）
        name_col (str): 姓名列名（默认为'姓名'）
        score_col (str): 成绩列名（默认为'最终成绩'）

    返回:
        pd.DataFrame: 合并后的DataFrame，包含学号、姓名和作业1成绩
    """
    try:
        # 验证输入DataFrame
        if id_col not in df_stu_info.columns:
            raise ValueError(f"学生信息DataFrame中缺少学号列: {id_col}")
        if name_col not in df_stu_info.columns:
            raise ValueError(f"学生信息DataFrame中缺少姓名列: {name_col}")
        if id_col not in df_homework.columns:
            raise ValueError(f"作业成绩DataFrame中缺少学号列: {id_col}")
        if score_col not in df_homework.columns:
            raise ValueError(f"作业成绩DataFrame中缺少成绩列: {score_col}")

        # 1. 从作业成绩中只保留需要的列（学号和成绩）
        # 重命名成绩列为"作业1"
        df_homework_selected = df_homework[[id_col, score_col]].rename(
            columns={score_col: '作业1'}
        )

        # 2. 合并学生信息和作业成绩（左连接）
        merged_df = pd.merge(
            df_stu_info[[id_col, name_col]],  # 只保留学号和姓名
            df_homework_selected,
            on=id_col,
            how='left'  # 左连接保留所有学生
        )

        # 3. 处理缺失值（将缺失成绩填充为0）
        merged_df['作业1'] = merged_df['作业1'].fillna(0)

        # 4. 如果需要，保存到Excel
        if output_path:
            merged_df.to_excel(output_path, index=False)
            print(f"✅ 数据合并完成! 文件已保存至: {output_path}")

        # 5. 打印统计信息
        total_students = len(merged_df)
        homework_present = merged_df['作业1'].ne(0).sum()
        print(f"总学生数: {total_students}")
        print(f"有作业成绩的学生数: {homework_present} ({homework_present / total_students:.1%})")
        print(f"缺失作业成绩的学生数: {total_students - homework_present}")

        return merged_df

    except Exception as e:
        print(f"❌ 合并失败: {str(e)}")
        raise


def merge_student_homeworks(df_stu_info, homework_dfs, output_path=None,
                            id_col='学号', name_col='姓名',
                            fill_value=0, validate_names=True):
    """
    合并多个学生作业成绩DataFrame

    参数:
        df_stu_info (pd.DataFrame): 学生信息DataFrame（包含完整学生名单）
        homework_dfs (list): 作业成绩DataFrame列表，每个包含学号、姓名和作业成绩列
        output_path (str): 输出Excel文件路径（可选）
        id_col (str): 学号列名（默认为'学号'）
        name_col (str): 姓名列名（默认为'姓名'）
        fill_value: 缺失值填充值（默认为0）
        validate_names: 是否验证姓名一致性

    返回:
        pd.DataFrame: 合并后的DataFrame，包含学号、姓名和所有作业成绩
    """
    try:
        # 验证输入DataFrame
        if id_col not in df_stu_info.columns:
            raise ValueError(f"学生信息DataFrame中缺少学号列: {id_col}")
        if name_col not in df_stu_info.columns:
            raise ValueError(f"学生信息DataFrame中缺少姓名列: {name_col}")

        # 1. 创建基础合并DataFrame（只包含学号和姓名）
        merged_df = df_stu_info[[id_col, name_col]].copy()

        # 2. 遍历所有作业DataFrame
        for i, df_homework in enumerate(homework_dfs, 1):
            # 验证当前作业DataFrame
            if id_col not in df_homework.columns:
                raise ValueError(f"作业{i}DataFrame中缺少学号列: {id_col}")

            # 获取作业成绩列名（假设是除了学号和姓名外的第一列）
            possible_score_cols = [col for col in df_homework.columns
                                   if col not in [id_col, name_col]]

            if not possible_score_cols:
                raise ValueError(f"作业{i}DataFrame中没有找到成绩列")

            # 使用第一个可能的成绩列
            score_col = possible_score_cols[0]
            new_col_name = f"作业{i}"

            # 选择并重命名成绩列
            df_homework_selected = df_homework[[id_col, score_col]].rename(
                columns={score_col: new_col_name}
            )

            # 合并当前作业成绩
            merged_df = pd.merge(
                merged_df,
                df_homework_selected,
                on=id_col,
                how='left'
            )

            # 填充缺失成绩
            merged_df[new_col_name] = merged_df[new_col_name].fillna(fill_value)

            # 姓名一致性检查（可选）
            if validate_names and name_col in df_homework.columns:
                # 临时合并以获取作业中的姓名
                temp_df = pd.merge(
                    merged_df[[id_col, name_col]],
                    df_homework[[id_col, name_col]].rename(columns={name_col: f'作业{i}_姓名'}),
                    on=id_col,
                    how='left'
                )

                # 检查姓名是否匹配
                name_mismatch = temp_df[
                    (temp_df[name_col] != temp_df[f'作业{i}_姓名']) &
                    (~temp_df[f'作业{i}_姓名'].isna())
                    ]

                if not name_mismatch.empty:
                    print(f"⚠️ 警告(作业{i}): 发现 {len(name_mismatch)} 条姓名不一致的记录")
                    print(name_mismatch)

            print(f"✅ 已合并作业{i}数据")

        # 4. 计算总成绩
        # 4.1. 选择作业列（假设列名以"作业"开头）
        homework_cols = [col for col in merged_df.columns if col.startswith('作业')]
        # 4.2. 计算总成绩并向上取整
        merged_df['总成绩'] = merged_df[homework_cols].sum(axis=1).apply(np.ceil).astype(int)
        # 4.3. 限制总成绩在[0,100)范围内
        merged_df['总成绩'] = merged_df['总成绩'].clip(lower=0, upper=99)  # 上限设为99确保小于100

        # 3. 如果需要，保存到Excel
        if output_path:
            merged_df.to_excel(output_path, index=False)
            print(f"\n✅ 所有作业合并完成! 文件已保存至: {output_path}")

        return merged_df

    except Exception as e:
        print(f"❌ 合并失败: {str(e)}")
        raise


if __name__ == "__main__":
    上机 = "上机1"
    se_files_name = get_files_name(f"{BASE_PATH_微服务}/{上机}/提交")
    # print(se_files_name)
    se_学号 = get_学号_segment(se_files_name)
    # print(se_学号)

    se_姓名 = get_姓名_segment(se_files_name)
    # print(se_姓名)

    se_real_日期 = get_real_日期_segment(se_files_name)
    se_real_date = convert_mmdd_to_date(se_real_日期)
    print(se_real_date)
